package strategy;

import algorithm.Greenwave;
import algorithm.Kdalgorithm;
import com.openatc.core.common.IErrorEnumImplInner;
import com.openatc.core.common.IErrorEnumImplOuter;
import com.openatc.core.model.InnerError;
import com.openatc.core.model.RESTRetBase;
import com.openatc.core.util.RESTRetUtils;
import com.openatc.model.model.Split;
import model.RouteIntsection;
import model.RouteOpt;
import model.RoutePara;

import java.util.*;
import java.util.logging.Logger;

/**
 * @Classname RouteOptStrategy
 * @Description
 * @Date 2022/5/29 17:44
 * @Created by panleilei
 */
public class RouteOptStrategy {

    private Kdalgorithm kdalgorithm = new Kdalgorithm();

    private Logger logger = Logger.getLogger(RouteOptStrategy.class.getSimpleName());

    /**
     * 红波协调
     * @param routePara
     * @return
     */
    public RESTRetBase getRedRouteOpt(RoutePara routePara) {


        RouteOpt routeOpt = new RouteOpt();
        routeOpt.setDevs(routePara.getDevs());

        int speed;
        String type = routePara.getDirection(); // 上行/下行
        // 上行
        if ("up".equals(type)){
            speed = routePara.getUpspeed();
        }
        // 下行
        else {
            speed = routePara.getDownspeed();
        }
        // 无法协调的情况:
        List<Greenwave> greenwaves = new LinkedList<>();
        // 1、没有协调路口
        if (routeOpt.getDevs().isEmpty()){
            routeOpt.setGreenwave(new ArrayList<>());
            return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5003);
//            return routeOpt;
        }
        // 2、路口小于3个
        if (routeOpt.getDevs().size() < 3){
//            routeOpt.setGreenwave(new ArrayList<>());
//            return routeOpt;
            InnerError innerError = RESTRetUtils.innerErrorObj(null, IErrorEnumImplInner.E_116, null);
            return RESTRetUtils.errorDetialObj(IErrorEnumImplOuter.E_5003,innerError);
        }
        // 路口id和路口顺序映射
        Map<String,Integer> agentidSortidMap = new HashMap<>();
        for (RouteIntsection routeIntsection : routeOpt.getDevs()){
            agentidSortidMap.put(routeIntsection.getAgentid(),routeIntsection.getSortid());
        }
        try {
            // 关键路口
            String keyintsid = routePara.getKeyintsid();
            if (keyintsid == null) {
                InnerError innerError = RESTRetUtils.innerErrorObj(null, IErrorEnumImplInner.E_113, null);
                return RESTRetUtils.errorDetialObj(IErrorEnumImplOuter.E_5003,innerError);
            }
            // 3、上行红波：关键路口为前两个
            if (routePara.getDirection().equals("up")){
                if (agentidSortidMap.get(keyintsid) < 3){
//                    routeOpt.setGreenwave(new ArrayList<>());
//                    return routeOpt;
                    InnerError innerError = RESTRetUtils.innerErrorObj(null, IErrorEnumImplInner.E_114, null);
                    return RESTRetUtils.errorDetialObj(IErrorEnumImplOuter.E_5003,innerError);
                }
            }
            // 4、下行红波：关键路口为后两个
            else {
                if (agentidSortidMap.get(keyintsid) > routeOpt.getDevs().size() - 2){
//                    Greenwave greenwave = new Greenwave(type,0,0, speed, 10);
//                    greenwaves.add(greenwave);
//                    routeOpt.setGreenwave(greenwaves);
//                    routeOpt.setGreenwave(new ArrayList<>());
//                    return routeOpt;
                    InnerError innerError = RESTRetUtils.innerErrorObj(null, IErrorEnumImplInner.E_115, null);
                    return RESTRetUtils.errorDetialObj(IErrorEnumImplOuter.E_5003,innerError);
                }
            }

            int offsets[] = new int[routePara.getDevs().size()]; // 保存优化后的相位差
            int intssplit[] = new int[routePara.getDevs().size()]; // 保存路口协调相位绿信比的值
            int intscycle[] = new int[routePara.getDevs().size()]; // 保存协调路口方案的周期
            int intsdistances[] = new int[routePara.getDevs().size()]; // 保存各路口的距离

            // 初始化各路口协调相位绿信比
            for (RouteIntsection dev : routeOpt.getDevs()) {
                // 保存周期
                intscycle[dev.getSortid() - 1] = dev.getFeature().getPatternList().get(0).getCycle();
                // 保存各路口相位差
                offsets[dev.getSortid() - 1] = dev.getFeature().getPatternList().get(0).getOffset();
                // 保存各路口距离
                intsdistances[dev.getSortid() - 1] = dev.getDistance();
                // 协调相位号
                int phaseno;
                if (routePara.getDirection().equals("up")) {
                    phaseno = dev.getForwardphaseid();
                } else {
                    phaseno = dev.getBackphaseid();
                }

                List<List<Split>> rings = dev.getFeature().getPatternList().get(0).getRings();
                // 获取参与协调相位的绿信比
                label:
                for (int i = 0; i < rings.size(); i++) {
                    for (Split split : rings.get(i)) {
                        if (phaseno == split.getId()) {
                            intssplit[dev.getSortid() - 1] = split.getValue();
                            break label;
                        }
                    }
                }
            }
            // 判断所有路口的周期是否全部相同
            boolean isAllCycleEqual = isAllCycleEqual(intscycle);
            if (!isAllCycleEqual)
//                return null;
                return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5002);

            // 关键路口序号
            int keyrouteNo = agentidSortidMap.get(keyintsid);
            // 获取需要协调的路口
            List<RouteIntsection> routeOptList;
            // 上行（第二个路口--关键路口的前一个路口）
            if ("up".equals(type)) {
                routeOptList = routeOpt.getDevs().subList(1, keyrouteNo - 1);
            }
            // 下行（最后一个路口--关键路口的后一个路口）
            else {
                routeOptList = routeOpt.getDevs().subList(keyrouteNo, routeOpt.getDevs().size());
            }

            // 公共周期
            int cycle = kdalgorithm.getRewWaveCycle(intscycle);
            // 计算每个路口的相位差
            // 上行
            if (routePara.getDirection().equals("up")) {
                for (RouteIntsection dev : routeOptList) {
                    int distance = dev.getDistance();
                    offsets = kdalgorithm.redwaveOffsetOptUp(distance, speed, intssplit, cycle, dev.getSortid(), offsets);
                }
            }
            // 下行
            else {
                Collections.reverse(routeOptList);
                for (RouteIntsection dev : routeOptList) {
                    offsets = kdalgorithm.redwaveOffsetOptDown(speed, intssplit, cycle, dev.getSortid(), offsets, intsdistances);
                }
                Collections.reverse(routeOptList);
            }

            for (RouteIntsection dev : routeOptList) {
                // 改变路口相位差
                dev.getFeature().getPatternList().get(0).setOffset(offsets[dev.getSortid() - 1]);
            }
            // 获取Wave
            Greenwave greenwave;
            if (routePara.getDirection().equals("up")) {
                int width = kdalgorithm.getMotorcadeWidth(intssplit);
                greenwave = kdalgorithm.getMUp(type, speed, width);
            } else {
                int width = kdalgorithm.getMotorcadeWidth(intssplit);
                greenwave = kdalgorithm.getMDown(type, speed, width);
            }
            greenwaves.add(greenwave);

            routeOpt.setGreenwave(greenwaves);
            return RESTRetUtils.successObj(routeOpt);
//            return routeOpt;
        } catch (Exception e){
            logger.info(e.toString());
            routeOpt.setGreenwave(new ArrayList<>());
            return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5003);
//            return routeOpt;
        }
    }

    /**
     * 判断周期长度是否一致
     * @param intscycle
     * @return
     */
    private boolean isAllCycleEqual(int[] intscycle) {
        if (intscycle.length == 0){
            return false;
        }
        int firstElement = intscycle[0];
        for (int i : intscycle){
            if (firstElement != i){
                return false;
            }
        }
        return true;
    }

    /**
     * 车队尾协调策略
     * @param routePara
     * @return
     */
    public RESTRetBase getLastOfMotorcadeRouteOpt(RoutePara routePara) {

        RouteOpt routeOpt = new RouteOpt();
        routeOpt.setDevs(routePara.getDevs());

        int speed;
        String type = routePara.getDirection(); // 上行/下行
        // 上行
        if (routePara.getDirection().equals("up")){
            speed = routePara.getUpspeed();
        }
        // 下行
        else {
            speed = routePara.getDownspeed();
        }
        // 无法协调的情况:
        List<Greenwave> greenwaves = new LinkedList<>();
        // 1、没有协调路口
        if (routeOpt.getDevs().isEmpty()){
//            routeOpt.setGreenwave(new ArrayList<>());
//            return routeOpt;
            return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5003);
        }
        // 2、路口小于3个
        if (routeOpt.getDevs().size() < 3){
//            routeOpt.setGreenwave(new ArrayList<>());
//            return routeOpt;
            InnerError innerError = RESTRetUtils.innerErrorObj(null, IErrorEnumImplInner.E_116, null);
            return RESTRetUtils.errorDetialObj(IErrorEnumImplOuter.E_5003,innerError);
        }
        // 路口id和路口顺序映射
        Map<String,Integer> agentidSortidMap = new HashMap<>();
        for (RouteIntsection routeIntsection : routeOpt.getDevs()){
            agentidSortidMap.put(routeIntsection.getAgentid(),routeIntsection.getSortid());
        }
        try {
            // 关键路口
            String keyintsid = routePara.getKeyintsid();
            if (keyintsid == null) {
                InnerError innerError = RESTRetUtils.innerErrorObj(null, IErrorEnumImplInner.E_113, null);
                return RESTRetUtils.errorDetialObj(IErrorEnumImplOuter.E_5003,innerError);
            }
            // 3、上行红波：关键路口为前两个
            if (routePara.getDirection().equals("up")){
                if (agentidSortidMap.get(keyintsid) < 3){
//                    routeOpt.setGreenwave(new ArrayList<>());
//                    return routeOpt;
                    InnerError innerError = RESTRetUtils.innerErrorObj(null, IErrorEnumImplInner.E_114, null);
                    return RESTRetUtils.errorDetialObj(IErrorEnumImplOuter.E_5003,innerError);
                }
            }
            // 4、下行红波：关键路口为后两个
            else {
                if (agentidSortidMap.get(keyintsid) > routeOpt.getDevs().size() - 2){
//                    routeOpt.setGreenwave(new ArrayList<>());
//                    return routeOpt;
                    InnerError innerError = RESTRetUtils.innerErrorObj(null, IErrorEnumImplInner.E_115, null);
                    return RESTRetUtils.errorDetialObj(IErrorEnumImplOuter.E_5003,innerError);
                }
            }

            int offsets[] = new int[routePara.getDevs().size()]; // 保存优化后的相位差
            int intssplit[] = new int[routePara.getDevs().size()]; // 保存路口协调相位绿信比的值
            int intscycle[] = new int[routePara.getDevs().size()]; // 保存协调路口方案的周期
            int intsdistances[] = new int[routePara.getDevs().size()]; // 保存各路口的距离

            // 初始化各路口协调相位绿信比
            for (RouteIntsection dev : routeOpt.getDevs()) {
                // 保存周期
                intscycle[dev.getSortid() - 1] = dev.getFeature().getPatternList().get(0).getCycle();
                offsets[dev.getSortid() - 1] = dev.getFeature().getPatternList().get(0).getOffset();
                // 保存各路口距离
                intsdistances[dev.getSortid() - 1] = dev.getDistance();
                // 协调相位号
                int phaseno;
                if (routePara.getDirection().equals("up")) {
                    phaseno = dev.getForwardphaseid();
                } else {
                    phaseno = dev.getBackphaseid();
                }

                List<List<Split>> rings = dev.getFeature().getPatternList().get(0).getRings();
                // 获取参与协调相位的绿信比
                label:
                for (int i = 0; i < rings.size(); i++) {
                    for (Split split : rings.get(i)) {
                        if (phaseno == split.getId()) {
                            intssplit[dev.getSortid() - 1] = split.getValue();
                            break label;
                        }
                    }
                }
            }
            boolean allCycleEqual = isAllCycleEqual(intscycle);
            if (!allCycleEqual)
                return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5002);

            // 关键路口序号
            int keyrouteNo = agentidSortidMap.get(keyintsid);
            // 获取需要协调的路口
            List<RouteIntsection> routeOptList;
            // 上行（第二个路口--关键路口的前一个路口）
            if (routePara.getDirection().equals("up")) {
                routeOptList = routeOpt.getDevs().subList(1, keyrouteNo - 1);
            }
            // 下行（最后一个路口--关键路口的后一个路口）
            else {
                routeOptList = routeOpt.getDevs().subList(keyrouteNo, routeOpt.getDevs().size());
            }


            // 公共周期
            int cycle = kdalgorithm.getRewWaveCycle(intscycle);
            // 计算每个路口的相位差
            // 上行
            if (routePara.getDirection().equals("up")) {
                for (RouteIntsection dev : routeOptList) {
                    int distance = dev.getDistance();
                    offsets = kdalgorithm.motorcadeOffsetOptUp(distance, speed, intssplit, cycle, dev.getSortid(), offsets);
                }
            }
            // 下行
            else {
                Collections.reverse(routeOptList);
                for (RouteIntsection dev : routeOptList) {
                    offsets = kdalgorithm.motorcadeOffsetOptDown(speed, intssplit, cycle, dev.getSortid(), offsets, intsdistances);
                }
                Collections.reverse(routeOptList);
            }

            for (RouteIntsection dev : routeOptList) {
                // 改变路口相位差
                dev.getFeature().getPatternList().get(0).setOffset(offsets[dev.getSortid() - 1]);
            }
            // 获取Wave
            Greenwave greenwave;
            if (routePara.getDirection().equals("up")) {
                int width = kdalgorithm.getMotorcadeWidth(intssplit);
                greenwave = kdalgorithm.getMUp(type, speed, width);
            } else {
                int width = kdalgorithm.getMotorcadeWidth(intssplit);
                greenwave = kdalgorithm.getMDown(type, speed, width);
            }
            greenwaves.add(greenwave);

            routeOpt.setGreenwave(greenwaves);
//            return routeOpt;
            return RESTRetUtils.successObj(routeOpt);
        } catch (Exception e){
            logger.info(e.toString());
//            routeOpt.setGreenwave(new ArrayList<>());
//            return routeOpt;
            return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5003);
        }
    }

    /**
     * 绿波协调
     * @param routePara
     * @return
     */
    public RESTRetBase getGreenRouteOpt(RoutePara routePara) {

        RouteOpt routeOpt = new RouteOpt();
        routeOpt.setDevs(routePara.getDevs());
        //计算优化
        double intslenth[] = new double[routePara.getDevs().size()]; //保存路口长度的值
        int intssplit[] = new int[routePara.getDevs().size()]; //保存路口协调相位绿信比的值

        int intsoffset[] = new int[routePara.getDevs().size()]; //保存优化后各路口的相位差的值
        int intsabs[] = new int[routePara.getDevs().size()];//各路口协调相位所属周期时间位置，用来计算绝对相位差
        int intscycle[] = new int[routePara.getDevs().size()];  // 保存各路口周期长度

        try {
            double intsvelup = 0.00;
            double intsveldown = 0.00;

            intsvelup = routePara.getUpspeed();
            intsveldown = routePara.getDownspeed();

            int phaseno = 0;
            int cycle = 100;
            List<RouteIntsection> deviceList = routePara.getDevs();
            for (RouteIntsection device : deviceList) {
                // 保存各路口周期
                intscycle[device.getSortid() - 1] = device.getFeature().getPatternList().get(0).getCycle();
                //得到距离
                intslenth[device.getSortid() - 1] = device.getDistance();

                //得到周期
                cycle = device.getFeature().getPatternList().get(0).getCycle();
                if (cycle == 0)
                    return null;
                //根据相位号得到协调相位值
                if (routePara.getDirection().equals("up"))
                    phaseno = device.getForwardphaseid();
                else if (routePara.getDirection().equals("down"))
                    phaseno = device.getBackphaseid();
                else
                    phaseno = device.getForwardphaseid();

                List<List<Split>> rings = device.getFeature().getPatternList().get(0).getRings();

                label:
                for (int i = 0; i < rings.size(); i++) {
                    List<Split> ring = rings.get(i);
                    for (Split split : ring) {
                        if (split.getId() == phaseno) {
                            intssplit[device.getSortid() - 1] = split.getValue();
                            break label;
                        } else {
                            intsabs[device.getSortid() - 1] += split.getValue();
                        }
                    }
                }

                while (intsabs[device.getSortid() - 1] >= cycle) {
                    intsabs[device.getSortid() - 1] -= cycle;
                }
            }

            boolean allCycleEqual = isAllCycleEqual(intscycle);
            if (!allCycleEqual)
//                return null;
                return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5002);
            kdalgorithm.setAftcycle(cycle);
            if (routePara.getDirection().equals("up"))
                intsoffset = kdalgorithm.offsetOpt(routePara.getDirection(), intslenth, intsvelup, intssplit);
            else if (routePara.getDirection().equals("down"))
                intsoffset = kdalgorithm.offsetOpt(routePara.getDirection(), intslenth, intsveldown, intssplit);
            else
                intsoffset = kdalgorithm.offsetByBiDirection(intslenth, intsvelup, intsveldown, intssplit);

            List<RouteIntsection> routeOptList = routeOpt.getDevs();

            for (RouteIntsection device : routeOptList) {

                //把每个路口相位差转成绝对相位差，start位置也会变化
                intsoffset[device.getSortid() - 1] -= intsabs[device.getSortid() - 1];
                if (intsoffset[device.getSortid() - 1] < 0)
                    intsoffset[device.getSortid() - 1] += cycle;

                device.getFeature().getPatternList().get(0).setOffset(intsoffset[device.getSortid() - 1]);
            }

            List<Greenwave> greenwaveList = new LinkedList<>();
            if (routePara.getDirection().equals("up"))
                greenwaveList.add(kdalgorithm.getGwup());
            else if (routePara.getDirection().equals("down"))
                greenwaveList.add(kdalgorithm.getGwdown());
            else {
                greenwaveList.add(kdalgorithm.getGwup());
                greenwaveList.add(kdalgorithm.getGwdown());
            }
            routeOpt.setGreenwave(greenwaveList);

            return RESTRetUtils.successObj(routeOpt);
//            return routeOpt;
        } catch (Exception e) {
            logger.info(e.toString());
            return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5003);
//            return routeOpt;
        }
    }

}
